#include "stdafx.h"
#include "CppUnitTest.h"

// pulls in all concepts:
#include <vex/iterable/concept/range_concepts.h>

#include <vex.tests/mstest/log_message.h>
#include <vex.tests/mstest/crt_mem_check.h>
#include <vex.tests/mstest/tests.range/range_archetype.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests {	namespace range_concept_check {

    namespace vi = vex::iterable;

    TEST_CLASS(range_concept_check)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.range.range_concept_check")
            END_TEST_CLASS_ATTRIBUTE();
    public:
        typedef range_archetype<vex::trivial_tag, int&> trivial_range_archetype;
        typedef range_archetype<vex::output_tag, int> output_range_archetype;
        typedef range_archetype<vex::one_pass_tag, int&> one_pass_range_archetype;
        typedef range_archetype<vex::forward_tag, int&> forward_range_archetype;
        typedef range_archetype<vex::bidirectional_tag, int&> bidirectional_range_archetype;
        typedef range_archetype<vex::random_access_tag, int&> random_access_range_archetype;

        TEST_METHOD_INITIALIZE(set_up)
        {
            range_archetype_value_holder<int>::s_value = 0;
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
            range_archetype_value_holder<int>::s_value = 0;
        }

        TEST_METHOD(range_archetype_satisfies_trivial_range_concept)
        {   
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<trivial_range_archetype>));
            {
                Assert::IsFalse(trivial_range_archetype().move_front());
            }

            // must fail at compile time:
            {
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::OutputRange<trivial_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<trivial_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<trivial_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<trivial_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<trivial_range_archetype>));
            }
        }

        TEST_METHOD(range_archetype_satisfies_output_range_concept)
        { 
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<output_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OutputRange<output_range_archetype, int>));
            {
                Assert::IsFalse(output_range_archetype().move_front());
                Assert::IsFalse(output_range_archetype().put(1));
            }

            // must fail at compile time:
            {
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<output_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<output_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<output_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<output_range_archetype>));
            }
        }

        TEST_METHOD(range_archetype_satisfies_one_pass_range_concept)
        {
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<one_pass_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<one_pass_range_archetype>));
            {
                Assert::IsFalse(one_pass_range_archetype().move_front());
                //Assert::AreEqual(int(), one_pass_range_archetype().front());
                one_pass_range_archetype().front() = 42;
            }

            // must fail at compile time:
            {
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<one_pass_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<one_pass_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<one_pass_range_archetype>));
            }
        }

        TEST_METHOD(range_archetype_satisfies_forward_range_concept)
        {
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<forward_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<forward_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<forward_range_archetype>));
            {
                Assert::IsFalse(forward_range_archetype().move_front());
                forward_range_archetype().front() = 42;
                auto t_saved = forward_range_archetype().save();
                Assert::IsTrue(std::is_same<decltype(t_saved), forward_range_archetype>::value);
            }

            // must fail at compile time:
            {
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<forward_range_archetype>));
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<forward_range_archetype>));
            }
        }

        TEST_METHOD(range_archetype_satisfies_bidirectional_range_concept)
        {   
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<bidirectional_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<bidirectional_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<bidirectional_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<bidirectional_range_archetype>));

            {
                Assert::IsFalse(bidirectional_range_archetype().move_front());
                bidirectional_range_archetype().front() = 42;
                auto t_saved = bidirectional_range_archetype().save();
                Assert::IsTrue(std::is_same<decltype(t_saved), bidirectional_range_archetype>::value);

                Assert::IsFalse(bidirectional_range_archetype().move_back());
                bidirectional_range_archetype().back() = 42;
            }

            // must fail at compile time:
            {
                //BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<bidirectional_range_archetype>));
            }
        }

        TEST_METHOD(range_archetype_satisfies_random_access_range_concept)
        {   
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::TrivialRange<random_access_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::OnePassRange<random_access_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<random_access_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<random_access_range_archetype>));
            BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<random_access_range_archetype>));

            {
                Assert::IsFalse(random_access_range_archetype().move_front());
                random_access_range_archetype().front() = 42;
                auto t_saved = random_access_range_archetype().save();
                Assert::IsTrue(std::is_same<decltype(t_saved), random_access_range_archetype>::value);

                Assert::IsFalse(random_access_range_archetype().move_back());
                random_access_range_archetype().back() = 42;

                Assert::IsFalse(random_access_range_archetype().move_back_of(0u));
                Assert::IsFalse(random_access_range_archetype().move_front_of(0u));
                random_access_range_archetype().at(0) = 42;
            }
        }
    };
}}